package com.stars.common.signature;

import lombok.extern.slf4j.Slf4j;
import sun.misc.BASE64Decoder;
import sun.misc.BASE64Encoder;

import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;
import java.io.*;
import java.security.*;
import java.security.interfaces.RSAPublicKey;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;

/**
 * @author : alex
 * @version ：1.0.0
 * @Date : create by 2017/11/10 14:49
 * @description ：rsa工具类
 * @note 注意事项
 */
@Slf4j
public class RsaUtil {

    /**
     * 签名算法
     */
    public static final String  SIGNATURE_ALGORITHM = "SHA1withRSA";
    /**
     * 加密算法RSA
     */
    public static final String  KEY_ALGORITHM       = "RSA";
    /**
     * RSA最大加密明文大小
     */
    private static final int    MAX_ENCRYPT_BLOCK   = 117;

    /**
     * RSA最大解密密文大小
     */
    private static final int    MAX_DECRYPT_BLOCK   = 128;

    /**
     * 获取公钥
     * @param filename
     * @return
     * @throws Exception
     */
    public static PublicKey getPublicKey(String filename) throws Exception {
//        InputStream resourceAsStream = this.getClass().getClassLoader().getResourceAsStream(filename);
        InputStream resourceAsStream = new FileInputStream(filename);
        DataInputStream dis = new DataInputStream(resourceAsStream);
        byte[] keyBytes = new byte[resourceAsStream.available()];
        dis.readFully(keyBytes);
        dis.close();
        X509EncodedKeySpec spec = new X509EncodedKeySpec(keyBytes);
        KeyFactory kf = KeyFactory.getInstance("RSA");
        return kf.generatePublic(spec);
    }

    /**
     * 获取密钥
     * @param filename
     * @return
     * @throws Exception
     */
    public static PrivateKey getPrivateKey(String filename) throws Exception {
//        InputStream resourceAsStream = this.getClass().getClassLoader().getResourceAsStream(filename);
        InputStream resourceAsStream = new FileInputStream(filename);
        DataInputStream dis = new DataInputStream(resourceAsStream);
        byte[] keyBytes = new byte[resourceAsStream.available()];
        dis.readFully(keyBytes);
        dis.close();
        PKCS8EncodedKeySpec spec = new PKCS8EncodedKeySpec(keyBytes);
        KeyFactory kf = KeyFactory.getInstance("RSA");
        return kf.generatePrivate(spec);
    }


    /**
     * 生成rsa公钥和密钥
     * @param publicKeyFilename
     * @param privateKeyFilename
     * @param password
     * @throws IOException
     * @throws NoSuchAlgorithmException
     */
    public static void generateKey(String publicKeyFilename,String privateKeyFilename,String password) throws IOException, NoSuchAlgorithmException {
        KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("RSA");
        SecureRandom secureRandom = new SecureRandom(password.getBytes());
        keyPairGenerator.initialize(1024, secureRandom);
        KeyPair keyPair = keyPairGenerator.genKeyPair();
        byte[] publicKeyBytes = keyPair.getPublic().getEncoded();
        FileOutputStream fos = new FileOutputStream(publicKeyFilename);
        fos.write(publicKeyBytes);
        fos.close();
        byte[] privateKeyBytes = keyPair.getPrivate().getEncoded();
        fos = new FileOutputStream(privateKeyFilename);
        fos.write(privateKeyBytes);
        fos.close();
    }

    /**
     * 微信支付取到的rsa证书格式是： 公钥格式PKCS#1  ，需要把证书转换成,PKCS#8
     * 命令 ： openssl rsa -RSAPublicKey_in -in <filename> -pubout
     * pem 公钥 加载
     * @return
     * @throws Exception
     */
    public static PublicKey GeneratePublicKeyFromPem(String cerFilePath) throws Exception {

        try {
            BASE64Decoder base64decoder = new BASE64Decoder();
            BufferedReader br = new BufferedReader(new FileReader(cerFilePath));
            String s = br.readLine();
            StringBuffer publickey = new StringBuffer();
            s = br.readLine();
            while (s.charAt(0) != '-') {
                publickey.append(s + "\r");
                s = br.readLine();
            }
            System.out.println("publickey="+publickey);
            byte[] keybyte = base64decoder.decodeBuffer(publickey.toString());
            KeyFactory kf = KeyFactory.getInstance("RSA");
            X509EncodedKeySpec keySpec = new X509EncodedKeySpec(keybyte);
            PublicKey publicKey = kf.generatePublic(keySpec);

            return publicKey;
        } catch (Exception e) {
            System.out.println("读pem文件生成公钥出错：" +e.toString());
            throw e;
        }
    }
    /**
     * 微信支付使用的RSA 加密方式
     * @param str
     * @return
     */
    public static String RsaEn(String str, String publicKeyStr){
        String result=null;
        try{
            PublicKey publicKey=GeneratePublicKeyFromPem(publicKeyStr);
            Cipher cipher = Cipher.getInstance("RSA/ECB/OAEPWITHSHA-1ANDMGF1PADDING");

            //cipher = Cipher.getInstance(keyAlgorithm);
            cipher.init(Cipher.ENCRYPT_MODE, publicKey); //公钥加密
            byte[] encrypt = cipher.doFinal(str.getBytes());

            result=new BASE64Encoder().encode(encrypt);
        }catch(Exception e){
            System.out.println("RSA加密出现异常："+e.toString());
            e.printStackTrace();
        }
        return result;
    }



    /**
     * 从文件中输入流中加载公钥
     *
     * @param path
     *            公钥输入流
     * @throws Exception
     *             加载公钥时产生的异常
     */
    public static String loadPublicKeyByFile(String path) throws Exception {
        try {
            BufferedReader br = new BufferedReader(new FileReader(path));
            String readLine = null;
            StringBuilder sb = new StringBuilder();

            while ((readLine = br.readLine()) != null) {
                if(readLine.charAt(0) != '-'){
                    sb.append(readLine);
                }

            }
            br.close();
            return sb.toString();
        } catch (IOException e) {
            throw new Exception("公钥数据流读取错误");
        } catch (NullPointerException e) {
            throw new Exception("公钥输入流为空");
        }
    }

    /**
     * 获取Base64后的RSA密文
     * @param str
     * @param publicKeyPKCS8
     * @return
     * @throws Exception
     */
    public static String getRSA(String str,String publicKeyPKCS8) throws Exception {
        byte[] cipherData=RSAEncrypt.encrypt(RSAEncrypt.loadPublicKeyByStr(publicKeyPKCS8),str.getBytes());
        String cipher= Base64.encode(cipherData);
        return cipher;

    }

    /**
     * 从字符串中加载公钥
     *
     * @param publicKeyStr
     *            公钥数据字符串
     * @throws Exception
     *             加载公钥时产生的异常
     */
    public static RSAPublicKey loadPublicKeyByStr(String publicKeyStr)
            throws Exception {
        try {
            System.out.println("公钥串：" + publicKeyStr);
            System.out.println(publicKeyStr);
            byte[] buffer = Base64.decode(publicKeyStr);
            // KeyFactory keyFactory = KeyFactory.getInstance("RSA");
            KeyFactory keyFactory = KeyFactory.getInstance("RSA");
            //RSA/ECB/OAEPWITHSHA-1ANDMGF1PADDING 无此算法
            X509EncodedKeySpec keySpec = new X509EncodedKeySpec(buffer);
            return (RSAPublicKey) keyFactory.generatePublic(keySpec);
        } catch (NoSuchAlgorithmException e) {
            throw new Exception("无此算法");
        } catch (InvalidKeySpecException e) {
            throw new Exception("公钥非法");
        } catch (NullPointerException e) {
            throw new Exception("公钥数据为空");
        }
    }

    /**
     * RSA公钥加密
     * @param publicKey
     * @param plainTextData
     * @return
     * @throws Exception
     */
    public static byte[] encrypt(RSAPublicKey publicKey, byte[] plainTextData)
            throws Exception {
        if (publicKey == null) {
            throw new Exception("加密公钥为空, 请设置");
        }
        Cipher cipher = null;
        try {
            // 使用默认RSA
            cipher = Cipher.getInstance("RSA");
            // byte[] estr=encrypt(str.getBytes(),pub,2048, 11,"RSA/ECB/PKCS1Padding");
            // cipher= Cipher.getInstance("RSA", new BouncyCastleProvider());
            cipher.init(Cipher.ENCRYPT_MODE, publicKey);
            byte[] output = cipher.doFinal(plainTextData);
            return output;
        } catch (NoSuchAlgorithmException e) {
            throw new Exception("无此加密算法");
        } catch (NoSuchPaddingException e) {
            e.printStackTrace();
            return null;
        } catch (InvalidKeyException e) {
            throw new Exception("加密公钥非法,请检查");
        } catch (IllegalBlockSizeException e) {
            throw new Exception("明文长度非法");
        } catch (BadPaddingException e) {
            throw new Exception("明文数据已损坏");
        }
    }


    public static void main(String[] args) throws Exception {
        //enerateKey("D:\\rsa\\user_key\\pub.key","D:\\rsa\\user_key\\pri.key","123456");
        //System.out.print(loadPublicKeyByFile("D://cert//publicKey//pkcs8PubKey.pem"));
        System.out.print(getRSA("data",loadPublicKeyByFile("D://cert//publicKey//pkcs8PubKey.pem")));
        //System.out.println(encryptByPublicKey("data".getBytes(), GeneratePublicKeyFromPem("D://cert//publicKey//pkcs8PubKey.pem")));
        //System.out.print(encryptByPublicKey("data".getBytes(),"D://cert//publicKey//pkcs8PubKey.pem"));
        //System.out.println(encrypt(loadPublicKeyByStr(loadPublicKeyByFile("D://cert//publicKey//pkcs8PubKey.pem")), "data".getBytes()));
        //System.out.print(Base64.encode(encrypt(loadPublicKeyByStr(loadPublicKeyByFile("D://cert//publicKey//pkcs8PubKey.pem")), "data".getBytes())));
        //System.out.println(RsaEn("data", "D://cert//publicKey//pkcs8PubKey.pem"));

    }
}
